package org.ttitfly.workflow.engine.basic.parser;

import java.util.ArrayList;
import java.util.List;

import org.ttitfly.workflow.engine.Action;
import org.ttitfly.workflow.engine.Activity;
import org.ttitfly.workflow.engine.AutoRunUnit;
import org.ttitfly.workflow.engine.Process;
import org.ttitfly.workflow.engine.RunUnit;
import org.ttitfly.workflow.engine.Transition;
import org.ttitfly.workflow.engine.assignment.Assignment;
import org.ttitfly.workflow.engine.assignment.DefaultAssignment;
import org.ttitfly.workflow.engine.basic.BasicAction;
import org.ttitfly.workflow.engine.basic.BasicActivity;
import org.ttitfly.workflow.engine.basic.BasicProcess;
import org.ttitfly.workflow.engine.basic.SubProcessActivity;
import org.ttitfly.workflow.engine.decider.ConditionsExpressionDecider;
import org.ttitfly.workflow.engine.decider.Decider;
import org.ttitfly.workflow.engine.decider.DefaultPermissionDecider;
import org.ttitfly.workflow.engine.mode.AutoTransition;
import org.ttitfly.workflow.engine.mode.Decision;
import org.ttitfly.workflow.engine.mode.Discriminator;
import org.ttitfly.workflow.engine.mode.Fork;
import org.ttitfly.workflow.engine.mode.Join;
import org.ttitfly.workflow.exception.ProcessDefineException;
import org.ttitfly.workflow.factory.ObjectFactory;
import org.ttitfly.workflow.factory.ObjectNameUnavailableException;
import org.ttitfly.workflow.handler.Handler;
import org.ttitfly.workflow.handler.adapter.RefletctMethodHandlerAdapter;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


/**
 * 解释流程定义，生成最基本的流程对象
 * 
 * @author 潘海春
 * @version 2009-9-22
 */
public class BasicProcessElementParser implements ProcessElementParser {

	/**
	 * 对象工厂
	 */
	private ObjectFactory objectFactory;

	/**
	 * 构造函数
	 * 
	 * @param objectFactory
	 */
	public BasicProcessElementParser(ObjectFactory objectFactory) {
		if (objectFactory == null) {
			throw new IllegalArgumentException("参数[objectFactory]不能为空！");
		}
		this.objectFactory = objectFactory;
	}

	@Override
	public Process parse(Element element) throws ProcessDefineException {
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name)) {
			throw new ProcessDefineException("流程名称不能为空！");
		}
		// 版本
		String versionStr = element.getAttribute("version");
		float version = 0.0F;
		if (versionStr != null && !"".equals(versionStr)) {
			version = Float.valueOf(versionStr);
		}

		// startActions
		List<Action> startActions = new ArrayList<Action>();
		Action defaultAction = null;
		NodeList startActionsNodes = element.getElementsByTagName("start-actions");
		if (startActionsNodes != null && startActionsNodes.getLength() > 0) {
			NodeList actionNodes = ((Element) startActionsNodes.item(0)).getElementsByTagName("action");
			if (actionNodes != null && actionNodes.getLength() > 0) {
				for (int i = 0; i < actionNodes.getLength(); i++) {
					Element actionElement = (Element) actionNodes.item(i);
					Action action = parseAction(actionElement);
					String defaultStr = actionElement.getAttribute("default");
					if (defaultStr != null && !"".equals(defaultStr) && "true".equalsIgnoreCase(defaultStr)) {
						if (defaultAction != null) {
							throw new ProcessDefineException("流程至多拥有一个默认启动action！");
						}
						defaultAction = action;
					} else {
						startActions.add(action);
					}
				}
			}
		}
		if (startActions.isEmpty()) {
			startActions = null;
		}
		// --- active handlers
		List<Handler> activeHandlers = null;
		NodeList activeHandlersNodes = element.getElementsByTagName("active-handlers");
		if (activeHandlersNodes != null && activeHandlersNodes.getLength() > 0) {
			activeHandlers = parseHandlers((Element) activeHandlersNodes.item(0));
		}

		// --- close handlers
		List<Handler> closeHandlers = null;
		NodeList closeHandlersNodes = element.getElementsByTagName("close-handlers");
		if (closeHandlersNodes != null && closeHandlersNodes.getLength() > 0) {
			closeHandlers = parseHandlers((Element) closeHandlersNodes.item(0));
		}

		BasicProcess process = new BasicProcess(name, version, defaultAction, startActions, activeHandlers, closeHandlers);
		List<RunUnit> runUnits = new ArrayList<RunUnit>();
		// activity
		NodeList activityNodes = element.getElementsByTagName("activity");
		if (activityNodes != null && activityNodes.getLength() > 0) {
			for (int i = 0; i < activityNodes.getLength(); i++) {
				runUnits.add(parseActivity(process, (Element) activityNodes.item(i)));
			}
		}
		// sub process
		NodeList subProcessNodes = element.getElementsByTagName("sub-process");
		if (subProcessNodes != null && subProcessNodes.getLength() > 0) {
			for (int i = 0; i < subProcessNodes.getLength(); i++) {
				runUnits.add(parseSubProcess(process, (Element) subProcessNodes.item(i)));
			}
		}

		// auto-run
		NodeList autoRunNodes = element.getElementsByTagName("auto-run");
		if (autoRunNodes != null && autoRunNodes.getLength() > 0) {
			for (int i = 0; i < autoRunNodes.getLength(); i++) {
				runUnits.add(parseAutoRunUnit(process, (Element) autoRunNodes.item(i)));
			}
		}
		
		// auto-transition
		NodeList autoTransitionNodes = element.getElementsByTagName("auto-transition");
		if (autoTransitionNodes != null && autoTransitionNodes.getLength() > 0) {
			runUnits.add(parseAutoTransition(process, (Element) autoTransitionNodes.item(0)));
		}

		// decision
		NodeList decisionNodes = element.getElementsByTagName("decision");
		if (decisionNodes != null && decisionNodes.getLength() > 0) {
			for (int i = 0; i < decisionNodes.getLength(); i++) {
				runUnits.add(parseDecision(process, (Element) decisionNodes.item(i)));
			}
		}

		// discriminator
		NodeList discriminatorNodes = element.getElementsByTagName("discriminator");
		if (discriminatorNodes != null && discriminatorNodes.getLength() > 0) {
			for (int i = 0; i < discriminatorNodes.getLength(); i++) {
				runUnits.add(parseDiscriminator(process, (Element) discriminatorNodes.item(i)));
			}
		}

		// fork
		NodeList forkNodes = element.getElementsByTagName("fork");
		if (forkNodes != null && forkNodes.getLength() > 0) {
			for (int i = 0; i < forkNodes.getLength(); i++) {
				runUnits.add(parseFork(process, (Element) forkNodes.item(i)));
			}
		}

		// join
		NodeList joinNodes = element.getElementsByTagName("join");
		if (joinNodes != null && joinNodes.getLength() > 0) {
			for (int i = 0; i < joinNodes.getLength(); i++) {
				runUnits.add(parseJoin(process, (Element) joinNodes.item(i)));
			}
		}

		// runUnits
		if (runUnits.isEmpty()) {
			throw new ProcessDefineException("运行单元不能为空！");
		}
		for (RunUnit unit : runUnits) {
			process.putUnit(unit);
		}
		return process;
	}

	/**
	 * 解释actions
	 * 
	 * @param actionsElement
	 * @return
	 */
	private Action parseAction(Element actionElement) {
		String className = actionElement.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (Action) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		// name
		String name = actionElement.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("动作的名字不能为空！");
		}
		// result
		String result = actionElement.getAttribute("result");
		if (result != null && "".equals(result = result.trim())) {
			result = null;
		}
		String status = actionElement.getAttribute("status");
		if (status != null && "".equals(status = status.trim())) {
			status = null;
		}

		List<Handler> handlers = null;
		NodeList handlersNodes = actionElement.getElementsByTagName("handlers");
		if (handlersNodes != null && handlersNodes.getLength() > 0) {
			handlers = parseHandlers((Element) handlersNodes.item(0));
		}
		return new BasicAction(name, handlers, result, status);
	}

	/**
	 * 解释handlers
	 * 
	 * @param handlersElement
	 * @return
	 */
	private List<Handler> parseHandlers(Element handlersElement) {
		NodeList handlerNodes = handlersElement.getElementsByTagName("handler");
		if (handlerNodes.getLength() > 0) {
			List<Handler> handlers = new ArrayList<Handler>();
			for (int i = 0; i < handlerNodes.getLength(); i++) {
				Element handlerElement = (Element) handlerNodes.item(i);
				String className = handlerElement.getAttribute("class");
				if (className != null && !"".equals(className = className.trim())) {
					try {
						handlers.add((Handler) objectFactory.getBean(className));
					} catch (ObjectNameUnavailableException e) {
						throw new ProcessDefineException(e);
					}
				} else {
					String invokeString = handlerElement.getAttribute("invoke");
					if (invokeString == null || "".equals(invokeString = invokeString.trim())) {
						throw new ProcessDefineException("处理程序配置错误！");
					}
					String objectName = "";
					String methodName = "";
					String[] args = null;
					if (invokeString.indexOf("#") > 0) {
						String[] s = invokeString.split("[#(,)]");
						objectName = s[0];
						methodName = s[1];
						args = new String[s.length - 2];
						for (int ai = 2; ai < s.length; ai++) {
							args[ai - 2] = s[ai];
						}
					} else {
						int methodSplitIndex = invokeString.lastIndexOf(".");
						objectName = invokeString.substring(0, methodSplitIndex);
						String[] ms = invokeString.substring(methodSplitIndex + 1).split("[(,)]");
						methodName = ms[0];
						args = new String[ms.length - 1];
						for (int ai = 1; ai < ms.length; ai++) {
							args[ai - 1] = ms[ai];
						}
					}
					try {
						handlers.add(new RefletctMethodHandlerAdapter(objectFactory.getBean(objectName), methodName, args));
					} catch (ObjectNameUnavailableException e) {
						throw new ProcessDefineException(e);
					}
				}
			}
			return handlers;
		}
		return null;
	}

	/**
	 * 解释活动
	 * 
	 * @param parent
	 * @param element
	 * @return
	 */
	protected Activity parseActivity(Process parent, Element element) {
		
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (Activity) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("活动的名称不能为空！");
		}

		// actions
		List<Action> actions = new ArrayList<Action>();
		Action defaultAction = null;
		NodeList actionNodes = element.getElementsByTagName("action");
		if (actionNodes != null && actionNodes.getLength() > 0) {
			for (int i = 0; i < actionNodes.getLength(); i++) {
				Element actionElement = (Element) actionNodes.item(i);
				Action action = parseAction(actionElement);
				String defaultStr = actionElement.getAttribute("default");
				if (defaultStr != null && !"".equals(defaultStr = defaultStr.trim()) && "true".equalsIgnoreCase(defaultStr)) {
					if (defaultAction != null) {
						throw new ProcessDefineException("活动[" + name + "]最多能有一个默认动作！");
					}
					defaultAction = action;
				} else {
					actions.add(action);
				}
			}
		}
		if (actions.isEmpty()) {
			actions = null;
		}

		// assignment
		Assignment assignment = null;
		String assignmentClass = element.getAttribute("assignment");
		if (assignmentClass == null || "".equals(assignmentClass = assignmentClass.trim())) {
			assignment = new DefaultAssignment(null);
		} else {
			try {
				assignment = (Assignment) objectFactory.getBean(assignmentClass);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}

		// permission decider
		Decider permissionDecider = null;
		String deciderClass = element.getAttribute("permission-decider");
		if (deciderClass == null || "".equals(deciderClass = deciderClass.trim())) {
			permissionDecider = new DefaultPermissionDecider();
		} else {
			try {
				permissionDecider = (Decider) objectFactory.getBean(deciderClass);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}

		// --- active handlers
		List<Handler> activeHandlers = null;
		NodeList activeHandlersNodes = element.getElementsByTagName("active-handlers");
		if (activeHandlersNodes != null && activeHandlersNodes.getLength() > 0) {
			activeHandlers = parseHandlers((Element) activeHandlersNodes.item(0));
		}

		// --- close handlers
		List<Handler> closeHandlers = null;
		NodeList closeHandlersNodes = element.getElementsByTagName("close-handlers");
		if (closeHandlersNodes != null && closeHandlersNodes.getLength() > 0) {
			closeHandlers = parseHandlers((Element) closeHandlersNodes.item(0));
		}
		return new BasicActivity(parent, name, defaultAction, actions, assignment, permissionDecider, activeHandlers, closeHandlers);
	}

	/**
	 * 解释子流程
	 * 
	 * @param parent
	 * @param element
	 * @return
	 */
	protected SubProcessActivity parseSubProcess(Process parent, Element element) {
		
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (SubProcessActivity) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("子流程活动名称不能为空！");
		}
		// 子流程名称
		String subProcessName = element.getAttribute("process");
		if (subProcessName == null || "".equals(subProcessName = subProcessName.trim())) {
			throw new ProcessDefineException("子流程名称不能为空！");
		}

		// 流程start action
		String startAction = element.getAttribute("start-action");
		if (startAction == null || "".equals(startAction.trim())) {
			startAction = null;
		}

		Action action = null;
		NodeList actionNodes = element.getElementsByTagName("action");
		if (actionNodes != null && actionNodes.getLength() > 0) {
			if (actionNodes.getLength() > 1) {
				throw new ProcessDefineException("子流程活动最多只有一个动作！");
			}
			action = parseAction((Element) actionNodes.item(0));
		}

		// --- active handlers
		List<Handler> activeHandlers = null;
		NodeList activeHandlersNodes = element.getElementsByTagName("active-handlers");
		if (activeHandlersNodes != null && activeHandlersNodes.getLength() > 0) {
			activeHandlers = parseHandlers((Element) activeHandlersNodes.item(0));
		}

		// --- close handlers
		List<Handler> closeHandlers = null;
		NodeList closeHandlersNodes = element.getElementsByTagName("close-handlers");
		if (closeHandlersNodes != null && closeHandlersNodes.getLength() > 0) {
			closeHandlers = parseHandlers((Element) closeHandlersNodes.item(0));
		}
		return new SubProcessActivity(parent, name, subProcessName, startAction, action, activeHandlers, closeHandlers);
	}

	/**
	 * 解释自动运行单元
	 * 
	 * @param parent
	 * @param element
	 * @return
	 */
	protected AutoRunUnit parseAutoRunUnit(Process parent, Element element) {
		
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (AutoRunUnit) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("自动运行单元的名称不能为空！");
		}

		Action action = null;
		NodeList actionNodes = element.getElementsByTagName("action");
		if (actionNodes != null && actionNodes.getLength() > 0) {
			if (actionNodes.getLength() > 1) {
				throw new ProcessDefineException("自动运行单元最多允许一个动作！");
			}
			action = parseAction((Element) actionNodes.item(0));
		}

		// --- active handlers
		List<Handler> activeHandlers = null;
		NodeList activeHandlersNodes = element.getElementsByTagName("active-handlers");
		if (activeHandlersNodes != null && activeHandlersNodes.getLength() > 0) {
			activeHandlers = parseHandlers((Element) activeHandlersNodes.item(0));
		}

		// --- close handlers
		List<Handler> closeHandlers = null;
		NodeList closeHandlersNodes = element.getElementsByTagName("close-handlers");
		if (closeHandlersNodes != null && closeHandlersNodes.getLength() > 0) {
			closeHandlers = parseHandlers((Element) closeHandlersNodes.item(0));
		}
		return new AutoRunUnit(parent, name, action, activeHandlers, closeHandlers);
	}
	
	/**
	 * 解释自动转向单元
	 * 
	 * @param process
	 * @param item
	 * @return
	 */
	protected AutoTransition parseAutoTransition(Process parent, Element element) {
		
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (AutoTransition) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("unit name can not be empty string!");
		}
		String toUnitsKey = element.getAttribute("toUnitsKey");
		if (toUnitsKey != null && "".equals(toUnitsKey = toUnitsKey.trim())) {
			toUnitsKey = null;
		}
		return new AutoTransition(parent, name, toUnitsKey);
	}

	/**
	 * decision
	 * 
	 * @param parent
	 * @param element
	 * @return
	 */
	protected Decision parseDecision(Process parent, Element element) {
		
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (Decision) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("决策器名称不能为空！");
		}
		List<Transition> transitions = parseTransitions(element.getElementsByTagName("transition"));

		String to = element.getAttribute("to");
		return new Decision(parent, name, to, transitions);
	}

	/**
	 * discriminator
	 * 
	 * @param parent
	 * @param element
	 * @return
	 */
	protected Discriminator parseDiscriminator(Process parent, Element element) {
		
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (Discriminator) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("鉴别器名称不能为空！");
		}
		List<Transition> transitions = parseTransitions(element.getElementsByTagName("transition"));

		String to = element.getAttribute("to");
		if (to == null || "".equals(to = to.trim())) {
			return new Discriminator(parent, name, transitions);
		}
		return new Discriminator(parent, name, transitions, to.split(","));
	}

	/**
	 * transitions
	 * 
	 * @param nodes
	 * @return
	 */
	protected List<Transition> parseTransitions(NodeList nodes) {
		if (nodes != null && nodes.getLength() > 0) {
			List<Transition> transitions = new ArrayList<Transition>();
			for (int i = 0; i < nodes.getLength(); i++) {
				transitions.add(parseTransition((Element) nodes.item(i)));
			}
			return transitions;
		}
		return null;
	}

	/**
	 * transition
	 * 
	 * @param element
	 * @return
	 */
	protected Transition parseTransition(Element element) {
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (Transition) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		String target = element.getAttribute("target");
		if (target == null || "".equals(target = target.trim())) {
			throw new ProcessDefineException("到达目标不能为空！");
		}
		Decider decider = null;
		String deciderClass = element.getAttribute("decider");
		if (deciderClass != null && !"".equals(deciderClass = deciderClass.trim())) {
			try {
				decider = (Decider) objectFactory.getBean(deciderClass);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		} else {
			decider = new ConditionsExpressionDecider(element.getAttribute("conditions"));
		}
		return new Transition(target, decider);
	}

	/**
	 * fork
	 * 
	 * @param parent
	 * @param element
	 * @return
	 */
	protected Fork parseFork(Process parent, Element element) {
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (Fork) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("分发器名称不能为空！");
		}
		String to = element.getAttribute("to");
		if (to == null || "".equals(to = to.trim())) {
			throw new ProcessDefineException("分发器必须有指向步骤！");
		}
		return new Fork(parent, name, to.split(","));
	}

	/**
	 * join
	 * 
	 * @param parent
	 * @param element
	 * @return
	 */
	protected Join parseJoin(Process parent, Element element) {
		String className = element.getAttribute("class");
		if (className != null && !"".equals(className = className.trim())) {
			try {
				return (Join) objectFactory.getBean(className);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		// 名称
		String name = element.getAttribute("name");
		if (name == null || "".equals(name = name.trim())) {
			throw new ProcessDefineException("合并器名称不能为空！");
		}
		String to = element.getAttribute("to");
		if (to == null || "".equals(to = to.trim())) {
			throw new ProcessDefineException("合并器必须有指向步骤");
		}
		Decider decider = null;
		String deciderClass = element.getAttribute("decider");
		if (deciderClass != null && !"".equals(deciderClass = deciderClass.trim())) {
			try {
				decider = (Decider) objectFactory.getBean(deciderClass);
				return new Join(parent, name, decider, to);
			} catch (ObjectNameUnavailableException e) {
				throw new ProcessDefineException(e);
			}
		}
		String conditions = element.getAttribute("conditions");
		if (conditions == null || "".equals(conditions = conditions.trim())) {
			throw new ProcessDefineException("合并器必须含有合并的步骤！");
		}
		return new Join(parent, name, conditions, to);
	}

}
